﻿using Function_Analyzer.Objects;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;

namespace Function_Analyzer.MathUtil
{
    class KhaoSat
    {
        const int DO_CHINH_XAC = 2;
        private static bool error;

        public static Ketquagioihan Tim_gh(string hs, string x0)
        {
            Ketquagioihan k = new Ketquagioihan();
            double x;
            string ytb;
            double[] yy=new double[5];
            string y, y1, y2;
            double x1;
            const double steps = 0.0000000001;

            MathCal.Init(hs, 2);

            switch (x0)
            {
                case "++": //tim gioi han khi x >> + vocuc, - vocuc
                case "--":
                    for (int i = 5; i <= 9; i++)
                    {
                        x = Math.Pow(10, i) * (x0 == "++" ? 1 : -1);  //a ;a chay den ++, -1 la chay den --
                        yy[i - 5] = MathCal.GetVal(x, 2);
                    }
                    if (Math.Round(yy[0], 3) == Math.Round(yy[1], 3) && Math.Round(yy[1], 3) == Math.Round(yy[2], 3) && Math.Round(yy[2], 3) == Math.Round(yy[3], 3) && Math.Round(yy[3], 3) == Math.Round(yy[4], 3))
                    {
                        k.gh = Math.Round(yy[0], 3).ToString();
                        k.ghduong = k.gh;
                        k.gham = k.gh;
                    }
                    else
                    {
                        if (yy[0] < yy[1] && yy[1] < yy[2] && yy[2] < yy[3] && yy[3] < yy[4])
                        {
                            k.gh = "+∞";
                            k.ghduong = "#";
                            k.gham = "#";
                        }
                        else if (yy[0] > yy[1] && yy[1] > yy[2] && yy[2] > yy[3] && yy[3] > yy[4])
                        {
                            k.gh = "-∞";
                            k.ghduong = "#";
                            k.gham = "#";
                        }
                        else{
                            k.gh = "#";
                            k.ghduong = "#";
                            k.gham = "#";
                        }
                    }
                    break;
                default:
                    x = Convert.ToDouble(x0);
                    y = MathCal.StrGetVal(x, 2);
                    
                    //tim gioi han x0+
                    x1 = x + steps;
                    y1 = MathCal.StrGetVal(x1, 2);
                    ytb = MathCal.StrGetVal(x1 + 0.01, 2);
                    if (y1 != "Error" && ytb != "Error")
                    {
                        if (y == "Error")
                        {
                            y2 = MathCal.StrGetVal(x1 + steps, 2);
                            if (Math.Abs(Math.Abs(Convert.ToDouble(y1)) - Math.Abs(Convert.ToDouble(y2))) > Math.Pow(10, 5)) //co tang dan thi tien den vo cung
                            { 
                                if (Convert.ToDouble(y1) > 0) k.ghduong = "+∞";
                                else k.gham = "-∞";
                            }
                            else k.ghduong = Math.Round(Convert.ToDouble(y1), 10).ToString();
                        }
                        else
                        {
                            if (Math.Abs(Math.Abs(Convert.ToDouble(y)) - Math.Abs(Convert.ToDouble(y1))) > Math.Pow(10, 5)) //co tang dan thi tien den vo cung
                            { 
                                if (Convert.ToDouble(y1) > 0) k.ghduong = "+∞";
                                else k.gham = "-∞";
                            }
                            else k.ghduong = Math.Round(Convert.ToDouble(y), 10).ToString();
                        }
                    }
                    else k.ghduong="#";

                    //tim gioi han x0-
                    x1=x-steps;
                    y1=MathCal.StrGetVal(x1, 2);
                    ytb=MathCal.StrGetVal(x1-0.01, 2);
                    if(y1!="Error" && ytb!="Error"){
                        if(y=="Error"){
                            y2=MathCal.StrGetVal(x1-steps, 2);
                            if (Math.Abs(Math.Abs(Convert.ToDouble(y1)) - Math.Abs(Convert.ToDouble(y2))) > Math.Pow(10, 5)) //co tang dan thi tien den vo cung
                            {
                                if (Convert.ToDouble(y1) > 0) k.ghduong = "+∞";
                                else k.gham = "-∞";
                            }
                            else k.ghduong = Math.Round(Convert.ToDouble(y1), 10).ToString();
                        }
                        else
                        {
                            if (Math.Abs(Math.Abs(Convert.ToDouble(y)) - Math.Abs(Convert.ToDouble(y1))) > Math.Pow(10, 5)) //co tang dan thi tien den vo cung
                            {
                                if (Convert.ToDouble(y1) > 0) k.ghduong = "+∞";
                                else k.gham = "-∞";
                            }
                            else k.ghduong = Math.Round(Convert.ToDouble(y), 10).ToString();
                        }
                    }

                    //tim gioi han x0
                    if (k.ghduong == k.gham)
                    {
                        k.gh = k.ghduong;
                    }
                    else
                    {
                        if (k.ghduong.Contains("∞") && k.gham.Contains("∞"))
                        {
                            k.gh = "∞";
                        }
                        else k.gh = "#";
                    }
                    break;
            }
            return k;
        }

        public static List<Diembatdinh> Tim_bd(string hs, double tu, double den, TextBlock tbTientrinh, ProgressBar proTientrinh, CoreDispatcher Dispatcher)
        {
            List<Diembatdinh> dbd=new List<Diembatdinh>();
            int tries;
            double guess, oldguess, slope;
            double x, y, lastx=0, lasty=0, nexty, x0;
            bool lasterr, currenterror;

            MathCal.Init(hs, 0);
            MathCal.Init(DaoHam.GetDerivative(hs), 1);

            tu = Math.Round(tu);
            den = Math.Round(den);
            x = tu;
            lasterr = false;

            //int value = 0;
            //int n = (int)((den - tu) / 0.2);
            //Debug.WriteLine(n);
            do
            {
                x = Math.Round(x, 3);
                //gian doan loai 2
                tries = 0;
                guess = x;
                do
                {
                    oldguess = guess;
                    y = MathCal.GetVal(guess, 0);
                    slope = MathCal.GetVal(guess, 1);
                    if (slope == 0) slope = 1;
                    guess = y / slope + guess;
                    if (Math.Abs(y) > 100 && Math.Abs(oldguess - guess) < 0.000000001) break;//tim thay
                    tries++;
                } while (tries < 20);
                if (tries < 20 && guess >= tu && guess <= den)
                {
                    for (int i = 0; i < dbd.Count; i++)
                    {
                        if (Math.Round(dbd[i].x, 6) == Math.Round(guess, 6)) goto boqua;
                    }
                    Diembatdinh d = new Diembatdinh();
                    d.x = guess;
                    d.loai = 2;
                    if (Math.Round(guess, 6) == Math.Round(guess, 3)) d.x = Math.Round(guess, 3);
                    dbd.Add(d);
                    goto boqua;
                }

                //tim cac diem gian doan loai 1, ko la bat dinh loai 2 thi la loai 1, ko tien den vo cung thi tien den huu han
                y = MathCal.GetVal(x, 0);
                currenterror = MathCal.error;
                nexty = MathCal.GetVal(x + 0.1, 1);
                if (!lasterr && currenterror && !MathCal.error)
                {
                    for (int i = 0; i < dbd.Count; i++)
                    {
                        if (Math.Round(dbd[i].x, 6) == Math.Round(x, 6)) goto boqua;
                    }
                    Diembatdinh d = new Diembatdinh();
                    d.x = x;
                    d.loai = 1;
                    dbd.Add(d);
                    goto boqua;
                }

                //tim cac diem bat dinh 1 ben: trai va phai
                if (!currenterror && lasterr)
                {
                    x0 = toiuudiembd(lastx, x, lasty, y, true);
                    for (int i = 0; i < dbd.Count; i++)
                    {
                        if (Math.Round(dbd[i].x, 6) == Math.Round(x0, 6)) goto boqua;
                    }
                    Diembatdinh d = new Diembatdinh();
                    d.x = x0;
                    d.loai = 0;
                    if (Math.Round(x0, 6) == Math.Round(x0, 3)) d.x = Math.Round(x0, 3);
                    dbd.Add(d);
                }
                if (currenterror && !lasterr && x != tu)
                {
                    x0 = toiuudiembd(lastx, x, lasty, y, false);
                    for (int i = 0; i < dbd.Count; i++)
                    {
                        if (Math.Round(dbd[i].x, 6) == Math.Round(x0, 6)) goto boqua;
                    }
                    Diembatdinh d = new Diembatdinh();
                    d.x = x0;
                    d.loai = 0;
                    if (Math.Round(x0, 6) == Math.Round(x0, 3)) d.x = Math.Round(x0, 3);
                    dbd.Add(d);
                }
                lasterr = currenterror;
                lastx = x;
                lasty = y;
            boqua:
                x += 0.2;

            //value++;
            //Debug.WriteLine("value" + value);
            //    int percent=(int)(value/n * 70);
            //    Debug.WriteLine("percent"+percent);
            //    if (percent % 10 == 0)
            //    {
            //        var vartbResult2a = Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
            //        {
            //            proTientrinh.Value = percent;
                        
            //        });
            //    }
            } while (x <= den);

            //sap xep cac diem bat dinh
            Diembatdinh tam;
            for (int i = 0; i < dbd.Count - 1;i++ )
                for (int j = i+1; j < dbd.Count; j++)
                    if (dbd[i].x < dbd[j].x)
                    {
                        tam = dbd[i];
                        dbd[i] = dbd[j];
                        dbd[j] = tam;
                    }

            return dbd;
        }

        private static double toiuudiembd(double x1, double x2, double y1, double y2, bool dbbentrai)
        {
            double x0 = (x1 + x2) / 2;
            if (x2 - x1 < 0.000000001) return x0;
            else
            {
                double y0 = MathCal.GetVal(x0, 0);
                if (MathCal.error == dbbentrai) return toiuudiembd(x0, x2, y0, y2, dbbentrai);
                else return toiuudiembd(x1, x0, y1, y0, dbbentrai);
            }
        }

        public static List<double> Tim_ng(string hs, double cdau, double ccuoi, string hs2, bool xetbdydh){
            List<double> nghiem = new List<double>();
            double root, slope, thestep, lastx=0, lasty=0, x, y, lastslope=0, slope1, slope2;
            const double rd=0.012354;

            if(hs2!="") hs="("+hs+")-(" +hs2+")";
            hs = DaoHam.EditedEqu(hs);
            MathCal.Init(hs, 0);
            MathCal.Init(DaoHam.GetDerivative(hs), 1);

            thestep=Math.Pow(10, -DO_CHINH_XAC+1);
            x=cdau+rd;
            slope = 200;
            do
            {
                y = MathCal.GetVal(x, 0);
                //loai 1
                if (y * lasty < 0)
                {
                    root = toiuunghiem(lastx, x, lasty, y, xetbdydh);
                    if (Math.Round(MathCal.GetVal(Math.Round(root, 6), 0), 3) != 0 && !xetbdydh) error = true;
                    if (MathCal.error) error = true;
                    if (!error)
                    {
                        nghiem.Add(root);
                    }
                }

                //loai 2
                slope = (y - lasty) / (x - lastx);
                if (slope * lastslope < 0 && lastslope * lasty <= 0 && Math.Abs(lasty) < 1)
                {
                    slope1 = MathCal.GetVal((lastx - thestep), 1);
                    slope2 = MathCal.GetVal(x, 1);
                    root = Math.Round(touchroot(lastx - thestep, x, slope1, slope2, xetbdydh), 4);
                    if (Math.Round(MathCal.GetVal(Math.Round(root, 6), 0), 5) != 0) error = true;
                    if (MathCal.error) error = true;
                    if (!error)
                    {
                        nghiem.Add(root);
                    }
                }

                lastx = x;
                lasty = y;
                lastslope = slope;
                x += thestep;
            } while (x <= ccuoi);

            //tim nghiem vs du doan
            //chua can

            return nghiem;
        }

        private static double toiuunghiem(double x1, double x2, double y1, double y2, bool xetbdydh)
        {
            error = false;
            double x0 = (x1 + x2) / 2;
            double y0 = MathCal.GetVal(x0, 0);
            if (!xetbdydh && Math.Abs(y0) > Math.Abs(y1) && Math.Abs(y0) > Math.Abs(y2))
            {
                error = true;
                return 0;
            }
            if (x0 - x1 < 0.000000001) return x0;
            else
            {
                if (y1 * y0 >= 0) return toiuudiembd(x0, x2, y0, y2, xetbdydh);
                else return toiuudiembd(x1, x0, y1, y0, xetbdydh);
            }
        }

        private static double touchroot(double x1, double x2, double y1, double y2, bool xetbdydh)
        {
            double x0 = (x1 + x2) / 2;
            double y0 = MathCal.GetVal(x0, 1);
            if (!xetbdydh && Math.Abs(y0) > Math.Abs(y1) && Math.Abs(y0) > Math.Abs(y2))
            {
                error = true;
                return 0;
            }
            if (x2 - x1 < 0.000000001) return x0;
            else
            {
                if (y1 * y0 >= 0) return touchroot(x0, x2, y0, y2, xetbdydh);
                else return touchroot(x1, x0, y1, y0, xetbdydh);
            }
        }
    }
}
